using System;
using System.Collections;
using System.Collections.Specialized;

namespace Copperline.Escriba
{
	/// <summary>
	/// Base class for aggregating loggers
	/// </summary>
	public class LogManager : LoggerBase
	{
		/// <summary>
		/// The collection of <c>ILogger</c>-derived
		/// instances that sink logging events
		/// </summary>
		protected ListDictionary loggers;

		/// <summary>
		/// Creates a new instance of the <c>LogManager</c> class
		/// </summary>
		public LogManager()
		{
			this.loggers = new ListDictionary();
		}

		/// <summary>
		/// Allows retrieval of a particular <c>ILogger</c>-derived
		/// logger through its instance name.
		/// </summary>
		/// <remarks>Rarely, it may be desirable to work directly
		/// with a particular logger, for example to disable it
		/// temporarily. More commonly, an instance of <c>LogManager</c>
		/// is initialized with loggers and then left alone.</remarks>
		public ILogger this [string sName]
		{
			get{ return (ILogger)loggers[ sName ]; }
			set{ AddLogger( sName, value ); }
		}

		/// <summary>
		/// Adds an instance of an <c>ILogger</c>-derived class
		/// to <c>this</c> <c>LogManagerBase</c>.
		/// </summary>
		/// <param name="sName">the name by which the logger will
		/// be indexed. Must be unique among the loggers that
		/// <c>this</c> <c>LogManagerBase</c> will manage.</param>
		/// <param name="logger">the instance of an <c>ILogger</c>-derived
		/// class that <c>this</c> <c>LogManager</c> will manage.</param>
		/// <remarks>A logger with the same name is automatically
		/// removed from the collection.</remarks>
		public void AddLogger( string sName, ILogger logger )
		{
			lock( loggers.SyncRoot )
			{
				if( logger != null )
				{
					RemoveLogger( sName );
					logger.Name = sName;
					loggers.Add( sName, logger );
				}
			}
		}

		/// <summary>
		/// Removes the named logger.
		/// </summary>
		/// <param name="sName">The name of the logger to remove</param>
		/// <remarks>No exception is thrown if the name does not
		/// map to a logger.</remarks>
		public void RemoveLogger( string sName )
		{
			lock( loggers.SyncRoot )
			{
				loggers.Remove( sName );
			}
		}

		/// <summary>
		/// Returns a <c>StringCollection</c> instance of the
		/// names of all the loggers managed by <c>this</c>
		/// <c>LogManager</c>.
		/// </summary>
		public StringCollection Keys
		{
			get 
			{
				ICollection col = null;
				lock( loggers.SyncRoot )
				{
					col = loggers.Keys;
					StringCollection scol = new StringCollection();
					foreach( Object obj in col )
					{
						scol.Add( (string)obj );
					}
					return scol;
				}
			}
		}

		public override void Write( LogEntry le )
		{
			foreach( ILogger logger in loggers.Values )
			{
				try
				{
					logger.Write( le );
				}
				catch( Exception ex )
				{
					logger.Fault = true;
					logger.LastError = new EscribaException("ProcessQueue failed for logger " + logger.Name, ex );
				}
			}
		}
	}
}
